package com.ruoyi.mdm.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.ruoyi.common.utils.Assert;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.mdm.domain.dto.MaterialCategoryDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import com.ruoyi.mdm.mapper.MaterialCategoryMapper;
import com.ruoyi.mdm.domain.MaterialCategory;
import com.ruoyi.mdm.service.IMaterialCategoryService;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;

/**
 * 物料类别Service业务层处理
 *
 * @author ruoyi
 * @date 2024-03-27
 */
@Service
@Slf4j
public class MaterialCategoryServiceImpl implements IMaterialCategoryService {

	@Resource
	private MaterialCategoryMapper materialCategoryMapper;

	/**
	 * 查询物料类别
	 *
	 * @param tableId 物料类别主键
	 * @return 物料类别
	 */
	@Override
	public MaterialCategory selectMaterialCategoryByTableId(Long tableId) {
		return materialCategoryMapper.selectMaterialCategoryByTableId(tableId);
	}

	/**
	 * 查询物料类别列表
	 *
	 * @param materialCategory 物料类别
	 * @return 物料类别
	 */
	@Override
	public List<MaterialCategory> selectMaterialCategoryList(MaterialCategory materialCategory) {
		return materialCategoryMapper.selectMaterialCategoryList(materialCategory);
	}

	/**
	 * 新增物料类别
	 *
	 * @param materialCategory 物料类别
	 * @return 结果
	 */
	@Override
	public int insertMaterialCategory(MaterialCategory materialCategory) {
		this.checkExist(materialCategory.getCategoryCode(), null);
		materialCategory.setTableId(IdUtils.fastId());
		return materialCategoryMapper.insertMaterialCategory(materialCategory);
	}

	/**
	 * 修改物料类别
	 *
	 * @param materialCategory 物料类别
	 * @return 结果
	 */
	@Override
	public int updateMaterialCategory(MaterialCategory materialCategory) {
		this.checkExist(materialCategory.getCategoryCode(), materialCategory.getTableId());
		return materialCategoryMapper.updateMaterialCategory(materialCategory);
	}

	/**
	 * 批量删除物料类别
	 *
	 * @param tableIds 需要删除的物料类别主键
	 * @return 结果
	 */
	@Override
	public int deleteMaterialCategoryByTableIds(Long[] tableIds) {
		return materialCategoryMapper.deleteMaterialCategoryByTableIds(tableIds);
	}

	/**
	 * 删除物料类别信息
	 *
	 * @param tableId 物料类别主键
	 * @return 结果
	 */
	@Override
	public int deleteMaterialCategoryByTableId(Long tableId) {
		return materialCategoryMapper.deleteMaterialCategoryByTableId(tableId);
	}

	@Override
	public String importData(List<MaterialCategoryDTO> dataList, boolean updateSupport) {
		Assert.isTrue(!dataList.isEmpty(), "数据列表为空");
		ImportBO importDTO = new ImportBO(dataList, updateSupport);
		importDTO.batchSave();
		return importDTO.report();
	}

	public void refreshAncestorList(Long parentId) {
		List<MaterialCategory> itemList = materialCategoryMapper.findItemHierarchyAncestor(parentId);
		itemList.forEach(item -> materialCategoryMapper.updateMaterialCategory(item));
	}

	class ImportBO {
		/** 原始数据列表 */
		private final List<MaterialCategoryDTO> dtoList;
		/** 根据父类编码进行分组 */
		private final Map<String, List<MaterialCategoryDTO>> childrenMap;
		/** 根据类别编码进行分组 */
		private final Map<String, MaterialCategoryDTO> categoryItemMap;
		/** 导入数据中最顶层节点编码 */
		private final List<String> topNodeCodeList;
		/** 导入数据中最顶层节点 */
		private final List<MaterialCategoryDTO> topNodeList;
		/** 最顶层节点在数据库中的父节点 */
		private Map<String, MaterialCategory> topNodeParentMap;
		/** 存在的节点 */
		private Map<String, MaterialCategory> existsCategoryMap;
		private boolean updateSupport;
		private int addSuccessQty = 0;
		private int updateSuccessQty = 0;
		private int failQty = 0;

		public String report() {
			return StringUtils.format("新增成功{}, 更新成功{}", addSuccessQty, updateSuccessQty);
		}
		public void batchSave() {
			this.checkDuplicateData();
			this.queryCategory();
			this.getTopNodeParent();
			this.recursionSave(topNodeList);
			refreshAncestorList(0L);
			// topNodeList.stream().forEach(node -> refreshAncestorList(node.getParentId()));
		}

		private Long getParentId(String parentCategoryCode) {
			// 初始父节点
			if (StringUtils.isEmpty(parentCategoryCode)) {
				return 0L;
			}
			// 从数据库中找到父节点
			if (topNodeParentMap.containsKey(parentCategoryCode)) {
				return topNodeParentMap.get(parentCategoryCode).getTableId();
			}
			// 从excel中找到父节点
			if (categoryItemMap.containsKey(parentCategoryCode)) {
				return categoryItemMap.get(parentCategoryCode).getTableId();
			}
			Assert.isTrue(true, "父节点类型{}不存在", parentCategoryCode);
			return null;
		}

		/**
		 * 递归保存
		 *
		 * @param dtoList dto列表
		 */
		private void recursionSave(List<MaterialCategoryDTO> dtoList) {
			if (ObjectUtils.isEmpty(dtoList)) {
				return;
			}
			dtoList.forEach(category -> {
				category.setParentId(this.getParentId(category.getParentCode()));
				if (existsCategoryMap.containsKey(category.getCategoryCode()) && !updateSupport) {
					failQty ++;
				} else if (existsCategoryMap.containsKey(category.getCategoryCode())) {
					category.setTableId(existsCategoryMap.get(category.getCategoryCode()).getTableId());
					updateSuccessQty += materialCategoryMapper.updateMaterialCategory(category.toPO());
				} else {
					category.setTableId(IdUtils.fastId());
					addSuccessQty += materialCategoryMapper.insertMaterialCategory(category.toPO());
				}
				this.recursionSave(childrenMap.get(category.getCategoryCode()));
			});
		}

		/**
		 * 获取顶部节点的父级
		 */
		private void getTopNodeParent() {
			List<String> parentCategoryList = topNodeCodeList.stream()
					.map(categoryCode -> categoryItemMap.get(categoryCode).getParentCode())
					.filter(StringUtils::isNotEmpty)
					.distinct()
					.collect(Collectors.toList());

			Map<String, MaterialCategory> parentCategoryMap = MaterialCategoryServiceImpl.this.findList(parentCategoryList)
					.stream()
					.collect(Collectors.toMap(MaterialCategory::getCategoryCode, Function.identity()));
			List<String> errorParentList = parentCategoryList.stream().filter(categoryCode -> !parentCategoryMap.containsKey(categoryCode)).collect(Collectors.toList());
			Assert.isTrue(errorParentList.isEmpty(), "父类编码{}在系统中不存在", errorParentList);

			this.topNodeParentMap = parentCategoryMap;
		}

		/**
		 * 检查数据
		 */
		private void checkDuplicateData() {
			Map<String, Long> codeQty = dtoList.stream().collect(Collectors.groupingBy(MaterialCategoryDTO::getCategoryCode, Collectors.counting()));
			List<String> codeRepeat = codeQty.keySet().stream().filter(key -> codeQty.get(key).intValue() > 1).collect(Collectors.toList());
			Assert.isTrue(codeRepeat.isEmpty(), "导入的数据中,类型编码重复{}", codeRepeat);
		}

		/**
		 * 获取数据库存在类别
		 */
		private void queryCategory() {
			Map<String, MaterialCategory> existsCategoryMap = MaterialCategoryServiceImpl.this.findList(new ArrayList<>(categoryItemMap.keySet()))
					.stream()
					.collect(Collectors.toMap(MaterialCategory::getCategoryCode, Function.identity()));
			this.existsCategoryMap = existsCategoryMap;
		}

		public ImportBO(List<MaterialCategoryDTO> dtoList, boolean updateSupport) {
			this.updateSupport = updateSupport;
			this.dtoList = dtoList;
			this.childrenMap = dtoList.stream().collect(Collectors.groupingBy(MaterialCategoryDTO::getParentCode));
			this.categoryItemMap = dtoList.stream().collect(Collectors.toMap(MaterialCategoryDTO::getCategoryCode, Function.identity()));
			// 用分组得出的parent去excel中查找, 如果在excel中找不到,则对应的children是顶层节点
			this.topNodeList = childrenMap.keySet().stream().filter(k -> !categoryItemMap.containsKey(k)).flatMap(k -> this.childrenMap.get(k).stream()).collect(Collectors.toList());
			this.topNodeCodeList = topNodeList.stream().map(MaterialCategoryDTO::getCategoryCode).collect(Collectors.toList());
		}
	}


	private void checkExist(String code, Long id) {
		MaterialCategory materialCategory = new MaterialCategory();
		materialCategory.setCategoryCode(code);
		materialCategory.setTableId(id);
		List<MaterialCategory> list = materialCategoryMapper.selectMaterialCategoryList(materialCategory);
		Assert.isTrue(list.isEmpty(), "类型编号{}已存在", code);
	}

	private List<MaterialCategory> findList(List<String> categoryCodeList) {
		MaterialCategory param = new MaterialCategory();
		param.setCategoryCodeList(categoryCodeList);
		return materialCategoryMapper.selectMaterialCategoryList(param);
	}
}
